package jp.co.menox.tmserver.service;

import java.util.List;

import org.slim3.datastore.CoreAttributeMeta;
import org.slim3.datastore.FilterCriterion;
import org.slim3.datastore.InMemoryFilterCriterion;
import org.slim3.datastore.StringAttributeMeta;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query.FilterOperator;

import jp.co.menox.tmcommon.model.CommonModelBase;
import jp.co.menox.tmserver.dao.TmServerDaoBase;

/**
 * サーバチーム用service基底クラス<br />
 * 共通データアクセスを切り出し
 * @author
 *
 * @param <T> model
 * @param <S> dao
 */
public abstract class TmServerServiceBase<T extends CommonModelBase, S extends TmServerDaoBase<T>> {

    protected S dao;
    
    /**
     * 全件取得
     * @return
     */
    public List<T> getAll() {
        return dao.getAll();
    }

    /**
     * 全件取得（論理削除済みのレコードを含む）
     * @return
     */
    @Deprecated
    public List<T> getAllWithDeleted() {
        return dao.getAllWithDeleted();
    }

    /**
     * 管理用IDに一致するレコードを取得する
     * @param id
     * @return
     */
    public T select(long id) {
        T model = dao.select(id);
        onSelected(model);
        return model;
    }

    /**
     * レコードを新規追加する
     * @param model
     * @return
     */
    public T insert(T model) {
        Key key = dao.insert(model);
        if (key == null) {
            return null;
        }
        model = dao.selectBg(key);
        onInserted(model);
        return model;
    }
    
    /**
     * レコードを更新する。引数のmodel中の管理用IDが一致するレコードを対象とする。
     * @param model
     * @return
     */
    public T update(T model) {
        if (model == null) {
            return null;
        }
        Key key = dao.update(model.getId(), model);
        if (key == null) {
            return null;
        }
        model = dao.selectBg(key);
        onUpdated(model);
        return model;
    }
    
    /**
     * レコードを削除（論理削除）する。引数のmodel中の管理用IDが一致するレコードを対象とする。
     * @param model
     * @return
     */
    public T delete(T model) {
        if (model == null) {
            return null;
        }
        Key key = dao.delete(model.getId());
        if (key == null) {
            return null;
        }
        model = dao.selectBg(key);
        onDeleted(model);
        return model;
    }
    
    /**
     * レコードを削除（物理削除）する。引数のmodel中の管理用IDが一致するレコードを対象とする。
     * @param model
     * @return
     */
    @Deprecated
    public T vanish(T model) {
        if (model == null) {
            return null;
        }
        Key key = dao.vanish(model.getId());
        if (key == null) {
            return null;
        }
        onVanished(model);
        return model;
    }
    

    // **** イベント処理用メソッド

    /**
     * selectイベント用
     * @param model
     */
    public void onSelected(T model){
    }

    /**
     * insertイベント用
     * @param model
     */
    public void onInserted(T model){
    }

    /**
     * updateイベント用
     * @param model
     */
    public void onUpdated(T model){
    }

    /**
     * deleteイベント用
     * @param model
     */
    public void onDeleted(T model){
    }

    /**
     * vanishイベント用
     * @param model
     */
    @Deprecated
    public void onVanished(T model){
    }

    
    /**
     * FilterOperatorよりFilterCriterionを取得する。<br />
     * 検索条件の「比較条件」と「比較値」から、queryに与えるfilterを生成する。<br />
     * （サービス部から任意のmodelにアクセスする可能性があるため、
     * serviceで使用するmodelとは別のmodelをtype parameterとして与える。）
     * @param <M> type of model
     * @param <V> type of value
     * @param operator filter operator
     * @param fieldMeta field(attribute)'s meta info
     * @param values compared value(s)
     * @return filter criterion
     */
    protected <M extends CommonModelBase, V> FilterCriterion getCriterion(
                FilterOperator operator, 
                CoreAttributeMeta<M, V> fieldMeta, 
                @SuppressWarnings("unchecked") V... values) {
        if (operator == null) {
            return null;
        }
        if (fieldMeta == null) {
            return null;
        }
        if (values == null) {
            return null;
        }

        FilterCriterion criterion = null;
        switch(operator) {
        case EQUAL:
            criterion = fieldMeta.equal(values[0]);
            break;
        case LESS_THAN:
            criterion = fieldMeta.lessThan(values[0]);
            break;
        case LESS_THAN_OR_EQUAL:
            criterion = fieldMeta.lessThanOrEqual(values[0]);
            break;
        case GREATER_THAN:
            criterion = fieldMeta.greaterThan(values[0]);
            break;
        case GREATER_THAN_OR_EQUAL:
            criterion = fieldMeta.greaterThanOrEqual(values[0]);
            break;
        case NOT_EQUAL:
            // 制限あり
            criterion = fieldMeta.notEqual(values[0]);
            break;
        case IN:
            // 制限あり
            criterion = fieldMeta.in(values);
            break;
        }
        
        return criterion;
    }

    /**
     * 文字列検索条件よりFilterCriterionを取得する。<br />
     * TODO: 設計に合わせて変更要。検索条件の種類は、今は完全一致／部分一致のみ<br />
     * （サービス部から任意のmodelにアクセスする可能性があるため、
     * serviceで使用するmodelとは別のmodelをtype parameterとして与える。）
     * @param <M> type of model
     * @param operator filter operator
     * @param fieldMeta field(attribute)'s meta info
     * @param values compared string
     * @return filter criterion(in-memory)
     */
    protected <M extends CommonModelBase> InMemoryFilterCriterion getCriterion(
                String operator, 
                StringAttributeMeta<M> fieldMeta, 
                String value) {
        if (operator == null) {
            return null;
        }
        if (fieldMeta == null) {
            return null;
        }
        if (value == null) {
            return null;
        }

        InMemoryFilterCriterion criterion = null;
        if (operator.equals("EQUAL")) {
            criterion = fieldMeta.equal(value);
        } else if (operator.equals("PARTIAL")) {
            criterion = fieldMeta.contains(value);
        }

        return criterion;
    }

}
